package edu.unlv.cs.ladders.web;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.roo.addon.web.mvc.controller.RooWebScaffold;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import edu.unlv.cs.ladders.entities.Event;
import edu.unlv.cs.ladders.entities.Tag;
import edu.unlv.cs.ladders.entities.Team;
import edu.unlv.cs.ladders.entities.Tournament;
import edu.unlv.cs.ladders.services.PermissionService;

@RooWebScaffold(path = "tournament", automaticallyMaintainView = false, formBackingObject = Tournament.class)
@RequestMapping("/tournament/**")
@Controller
public class TournamentController {
	@PreAuthorize("hasRole('ROLE_ADMIN')")
    @RequestMapping(value = "/tournament", method = RequestMethod.POST)
    public String create(@Valid Tournament tournament, BindingResult result, ModelMap modelMap) {
        if (tournament == null) throw new IllegalArgumentException("A tournament is required");
        if (result.hasErrors()) {
            modelMap.addAttribute("tournament", tournament);
            modelMap.addAttribute("tags", Tag.findAllTags());
            modelMap.addAttribute("teams", Team.findAllTeams());
            modelMap.addAttribute("tournament_startDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
            modelMap.addAttribute("tournament_endDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
            return "tournament/create";
        }
        tournament.persist();
        for(Tag tag : tournament.getTags()){
        	tag.addTournament(tournament);
        }
        this.getPermissionService().setDefaultPermissions(tournament);
        return "redirect:/tournament/" + tournament.getId();
    }
	
    @RequestMapping(value = "/tournament", method = RequestMethod.GET)
    public String list(@RequestParam(value = "page", required = false) Integer page, 
    		@RequestParam(value = "size", required = false) Integer size, 
    		@RequestParam(value = "tags", required = false) String[] tags, 
    		ModelMap modelMap) {
    	List<Tournament> tournaments = null;
    	List<Tag> filterTags = getTagsByNames(tags);
    	assert filterTags != null : "getTagsByNames should not have returned a null list.";
    	
        if (page != null || size != null) {
            int sizeNo = size == null ? 10 : size.intValue();
            tournaments = Tournament.findTournamentEntries(page == null ? 0 : (page.intValue() - 1) * sizeNo, sizeNo);
            float nrOfPages = (float) Tournament.countTournaments() / sizeNo;
            modelMap.addAttribute("maxPages", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));
        } else {
        	tournaments = Tournament.findAllTournaments();
        }
        tournaments = new ArrayList<Tournament>(this.filterByTags(tournaments, filterTags));
        modelMap.addAttribute("tags",Tag.findAllTags());
        modelMap.addAttribute("selectedTags",filterTags);
        modelMap.addAttribute("tournaments", tournaments);
        modelMap.addAttribute("tournament_startDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        modelMap.addAttribute("tournament_endDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        return "tournament/list";
    }

    @RequestMapping(value = "/tournament/{id}", method = RequestMethod.GET)
    public String show(@PathVariable("id") Long id, ModelMap modelMap) {
        if (id == null) throw new IllegalArgumentException("An Identifier is required");
        Tournament tournament = Tournament.findTournament(id);
        List<Event> events = getUpcomingEvents(tournament);
        modelMap.addAttribute("upcomingEvents",events);
        modelMap.addAttribute("tournament_startDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        modelMap.addAttribute("tournament_endDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        modelMap.addAttribute("event_dateOfEvent_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        modelMap.addAttribute("tournament", tournament);
        return "tournament/show";
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @RequestMapping(value = "/tournament/form", method = RequestMethod.GET)
    public String createForm(ModelMap modelMap) {
        modelMap.addAttribute("tournament", new Tournament());
        modelMap.addAttribute("tags", Tag.findAllTags());
        modelMap.addAttribute("teams", Team.findAllTeams());
        modelMap.addAttribute("tournament_startDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        modelMap.addAttribute("tournament_endDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        return "tournament/create";
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @RequestMapping(method = RequestMethod.PUT)
    public String update(@Valid Tournament tournament, BindingResult result, ModelMap modelMap) {
        if (tournament == null) throw new IllegalArgumentException("A tournament is required");
        if (result.hasErrors()) {
            modelMap.addAttribute("tournament", tournament);
            modelMap.addAttribute("tags", Tag.findAllTags());
            modelMap.addAttribute("teams", Team.findAllTeams());
            modelMap.addAttribute("tournament_startDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
            modelMap.addAttribute("tournament_endDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
            return "tournament/update";
        }
        tournament.merge();
        return "redirect:/tournament/" + tournament.getId();
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @RequestMapping(value = "/tournament/{id}/form", method = RequestMethod.GET)
    public String updateForm(@PathVariable("id") Long id, ModelMap modelMap) {
        if (id == null) throw new IllegalArgumentException("An Identifier is required");
        modelMap.addAttribute("tournament", Tournament.findTournament(id));
        modelMap.addAttribute("tags", Tag.findAllTags());
        modelMap.addAttribute("teams", Team.findAllTeams());
        modelMap.addAttribute("tournament_startDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        modelMap.addAttribute("tournament_endDate_date_format", org.joda.time.format.DateTimeFormat.patternForStyle("S-", org.springframework.context.i18n.LocaleContextHolder.getLocale()));
        return "tournament/update";
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @RequestMapping(value = "/tournament/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable("id") Long id, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "size", required = false) Integer size) {
        if (id == null) throw new IllegalArgumentException("An Identifier is required");
        Tournament.findTournament(id).remove();
        return "redirect:/tournament?page=" + ((page == null) ? "1" : page.toString()) + "&size=" + ((size == null) ? "10" : size.toString());
    }
    
    @SuppressWarnings("unchecked")
	private List<Event> getUpcomingEvents(Tournament tournament){
    	return Event.findEventsByTournamentAndDateOfEventGreaterThanEquals(tournament, Calendar.getInstance()).getResultList();
    }

	/**
	 * @param tagNames
	 */
	private List<Tag> getTagsByNames(String[] names) {
		List<Tag> tags = Tag.findAllTags();
		List<Tag> filteredTags = new ArrayList<Tag>();
    	if(names!=null){
    		List<String> namesList = new ArrayList<String>(Arrays.asList(names));
    		for(Iterator<Tag> it=tags.iterator();it.hasNext();){
    			Tag tag = it.next();
    			if(namesList.contains(tag.getName())){
    				filteredTags.add(tag);
    			}
    		}
    	}
    	return filteredTags;
	}
    
    private Collection<Tournament> filterByTags(List<Tournament> tournaments, List<Tag> tags){
    	assert tags != null : "tags should not be null.";
    	assert tournaments != null : "tournament should not be null.";
    	Set<Tournament> filteredTournaments = new HashSet<Tournament>();
    	if(tags.size()>0){
	    	for(Tournament currentTournament : tournaments){
	    		for(Tag currentTag : currentTournament.getTags()){
	    			if(tags.contains(currentTag)){
	    				filteredTournaments.add(currentTournament);
	    			}
	    		}
	    	}
	    	return filteredTournaments;
    	} else {
    		return tournaments;
    	}
    }
    private PermissionService permissionService;
	@Autowired
	public void setPermissionService(PermissionService permissionService) {
		this.permissionService = permissionService;
	}
	public PermissionService getPermissionService() {
		return permissionService;
	}

}
